#pragma once

#include <iostream>
#include <filesystem>       // 文件处理 C++17 标准
#include <memory>
#include <fstream>
#include <sstream>
#include <ctime>
#include <string>
#include <unistd.h>
#include "Mutex.hpp"

namespace logModule
{
    // 采用 策略模式(C++多态) 将写入方式分开
    // 1. 控制台写入
    // 2. 文件写入

    // 策略基类
    class LogStrategy
    {
    public:

        LogStrategy() = default;
        ~LogStrategy() = default;

        virtual void SyncLog(const std::string& message) = 0;
    };

    // 控制台打印日志策略子类
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        
        ConsoleLogStrategy() = default;
        ~ConsoleLogStrategy() = default;

        virtual void SyncLog(const std::string& message) override
        {
            _mutex.lock();                  // 加锁
            std::cout << message << std::endl;
            _mutex.unlock();
        }

    private:
        
        mutexModule::Mutex _mutex;
    };

    const std::string defaultPath = ".";
    const std::string defaultName = "my.log";
    const std::string gsep = "\r\n";

    // 文件打印日志策略子类
    class FileLogStrategy : public LogStrategy
    {
    public:

        ~FileLogStrategy() = default;

        FileLogStrategy(const std::string& path = defaultPath, const std::string& name = defaultName)
            :_path(path)
            ,_name(name)
        {
            if (std::filesystem::exists(_path))             // 查看路径是否存在，存在即返回
            {
                return;
            }

            try
            {
                std::filesystem::create_directories(_path); // 创建指定路径
            }
            catch(const std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << std::endl;
            }
        }

        virtual void SyncLog(const std::string& message) override
        {
            _mutex.lock();                  // 加锁
            
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _name;
            std::ofstream of(filename, std::ios::app);
            if (of.is_open() == false)
            {
                return;
            }

            of << message << gsep;

            of.close();

            _mutex.unlock();
        }
    
    private:

        mutexModule::Mutex _mutex;
    
        std::string _path;
        std::string _name;
    };

    enum LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL,
    };
    
    std::string LevelToStr(LogLevel level)
    {
        switch (level)
        {
            case LogLevel::DEBUG:
                return "DEBUG";
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";
            default:
                return "UNKNOW";
        }
    }

    std::string getCurrentTime()
    {
        time_t theTime = time(nullptr);
        struct tm timeData;

        localtime_r(&theTime, &timeData);

        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer), "%04d-%02d-%02d %02d:%02d:%02d", 
            timeData.tm_year + 1900,
            timeData.tm_mon + 1,
            timeData.tm_mday,
            timeData.tm_hour,
            timeData.tm_min,
            timeData.tm_sec
    );
        return timebuffer;
    }

    class Logger                // 日志管理
    {
    public:
    
        class LogMessage        // 单个日志消息
        {
        public:

            LogMessage(LogLevel level, const std::string& file_name, int line, Logger& logger)
                :_cur_time(getCurrentTime())
                ,_level(LevelToStr(level))
                ,_pid(getpid())
                ,_file_name(file_name)
                ,_line(line)
                ,_logger(logger)
            {
                _messageSum << '[' << _cur_time << "] "
                            << '[' << _level << "] "
                            << '[' << _pid << "] "
                            << '[' << _file_name << "] "
                            << '[' << _line << "] ";
            }

            template<class Type>
            LogMessage& operator<<(const Type& message)                 // 输出流式的输出信息
            {
                _messageSum << message;
                return *this;
            }

            ~LogMessage()
            {
                if (_logger._log_strategy == nullptr)
                {
                    return;
                }

                _logger._log_strategy->SyncLog(_messageSum.str());      // 消息打印
            }
            
        private:
            
            std::string _cur_time;          // 当前时间
            std::string _level;             // 日志消息级别
            pid_t _pid;                     // PID
            std::string _file_name;         // 所在文件名
            int _line;                      // 所在行号
            
            std::stringstream _messageSum;  // 汇合信息(stringstream 会将 << 得到的信息转为字符串)
            Logger& _logger;
        };

    public:

        Logger()
        {
            enableConsoleLogStrategy();
        }

        void enableConsoleLogStrategy()
        {
            _log_strategy = std::make_unique<ConsoleLogStrategy>();
        }

        void enableFileLogStrategy()
        {
            _log_strategy = std::make_unique<FileLogStrategy>();
        }
        
        // 将信息返回到程序员代码层面，再在 LogMessage 里实现 << 重载，使用 << 来输入到指定日志里
        LogMessage operator()(LogLevel level, const std::string& file_name, int line)
        {
            return LogMessage(level, file_name, line, *this);
        }

        ~Logger() = default;

    private:

        std::unique_ptr<LogStrategy> _log_strategy;
    };

    class Singleton                 // 饿汉单例模式
    {
    public:
        
        Singleton(const Singleton&) = delete;
        Singleton& operator=(const Singleton&) = delete;

        static Logger* getInstance()
        {
            return &logger;
        }

    private:

        Singleton() = default;
        static Logger logger;
    };

    Logger Singleton::logger;
}

#define LOG(level) logModule::Singleton::getInstance()->operator()(level, __FILE__, __LINE__)
#define ENABLE_CONSOLE_LOG_STRATEGY() logModule::Singleton::getInstance()->enableConsoleLogStrategy()
#define ENABLE_FILE_LOG_STRATEGY() logModule::Singleton::getInstance()->enableFileLogStrategy()
